package nl.cwi.arne.ui.parts;

import java.util.ArrayList;
import java.util.List;

import nl.cwi.arne.Network;
import nl.cwi.arne.NetworkActivation;
import nl.cwi.arne.diagram.part.ArneDiagramEditor;
import nl.cwi.arne.ui.actions.ClearActivationsAction;
import nl.cwi.arne.ui.actions.GenerateActivationsAction;
import nl.cwi.arne.ui.jobs.ShowLTSJob;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.IPageListener;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.ViewPart;

/**
 * Activations view.
 * @author Christian Krause
 */
public class ActivationsView extends ViewPart {

	// View ID.
	public static final String ID = "nl.cwi.arne.ui.views.activations";
	
	// Actions.
	private Action generateAction, clearAction;
	
	// Current network.
	private Network network;
	
	// Editor instance:
	private ArneDiagramEditor editor;
	
	// Table for displaying the activations.
	private Table table;
	
	// Table synchronizer.
	private ActivationsTableSynchronizer synchronizer;
	
	/*
	 * (non-Javadoc)
	 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
	 */
	@Override
	public void createPartControl(Composite parent) {
		
		// Container composite:
		Composite composite = new Composite(parent, SWT.NONE);
		composite.setLayout(new FillLayout());
		
		// The table:
		table = new Table(composite, SWT.FULL_SELECTION | SWT.MULTI | SWT.VIRTUAL | SWT.BORDER);
		table.setHeaderVisible(true);
		table.addSelectionListener(tableListener);
		table.addKeyListener(keyListener);

	}
	
	/*
	 * (non-Javadoc)
	 * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite)
	 */
	@Override
	public void init(IViewSite site) throws PartInitException {
		super.init(site);
		
		// Initialize the actions:
		generateAction = new GenerateActivationsAction(site);
		generateAction.setEnabled(false);
		site.getActionBars().getToolBarManager().add(generateAction);

		clearAction = new ClearActivationsAction(site);
		clearAction.setEnabled(false);
		site.getActionBars().getToolBarManager().add(clearAction);

		// Selection listener:
		site.getWorkbenchWindow().getSelectionService().addSelectionListener(selectionListener);
		site.getWorkbenchWindow().addPageListener(pageListener);
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.eclipse.ui.part.WorkbenchPart#dispose()
	 */
	@Override
	public void dispose() {
		
		// Remove listeners:
		getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(selectionListener);
		getSite().getWorkbenchWindow().removePageListener(pageListener);
		if (!table.isDisposed()) {
			table.removeSelectionListener(tableListener);
			table.removeKeyListener(keyListener);
		}
		
		// Dispose synchronizer:
		synchronizer.dispose();
		network = null;
		editor = null;
		
	}
	
	public void setNetwork(Network network) {
		if (this.network==network) return;
		table.clearAll();
		if (this.network!=null) {
			synchronizer.dispose();
			this.network.eAdapters().remove(networkListener);
		}
		this.network = network;
		if (network!=null) {
			synchronizer = new ActivationsTableSynchronizer(network, table);
			network.eAdapters().add(networkListener);
		}
		updateTitle();
		generateAction.setEnabled(network!=null);
		clearAction.setEnabled(network!=null);
	}

	/*
	 * Handle a double-click on a table item
	 */
	protected void handleDoubleClick(TableItem item) {
		int index = table.indexOf(item);
		NetworkActivation activation = network.getActivations().get(index);
		ShowLTSJob job = new ShowLTSJob(activation);
		job.schedule();
	}

	/*
	 * Delete the currently selected activations.
	 */
	protected void handleDelete() {
		
		// Collect activations to be deleted:
		if (network==null) return;
		final List<NetworkActivation> delete = new ArrayList<NetworkActivation>();
		int[] indices = table.getSelectionIndices();
		for (int i=0; i<indices.length; i++) {
			delete.add(network.getActivations().get(indices[i]));
		}
		if (delete.isEmpty()) return;
		
		// Delete command:
		AbstractTransactionalCommand command = new AbstractTransactionalCommand(editor.getEditingDomain(), "Delete", null) {
			@Override
			protected CommandResult doExecuteWithResult(
					IProgressMonitor monitor, IAdaptable info)
					throws ExecutionException {
				network.getActivations().removeAll(delete);
				return CommandResult.newOKCommandResult();
			}
		};
		
		// Execute the command:
		try {
			command.execute(new NullProgressMonitor(), null);
			table.deselectAll();
			table.setItemCount(0);
			table.setItemCount(network.getActivations().size());
		} catch (ExecutionException e) {
			e.printStackTrace();
			MessageDialog.openError(getSite().getShell(), "Delete", "Error deleting table entries: " + e);
		}
		
	}

	/*
	 * Update the title.
	 */
	protected void updateTitle() {
		String count = (network==null || network.getActivations().size()==0) ? "" : " (" + network.getActivations().size() + ")";
		setPartName("Activations" + count);
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
	 */
	@Override
	public void setFocus() {
		table.setFocus();
	}
	
	private void setPart(IWorkbenchPart part) {
		if (part instanceof ArneDiagramEditor) {
			editor = (ArneDiagramEditor) part;
			setNetwork(editor.getNetwork());
		}
	}
	
	// Selection listener:
	private ISelectionListener selectionListener = new ISelectionListener() {
		public void selectionChanged(IWorkbenchPart part, ISelection selection) {
			setPart(part);
		}
	};
	
	// Part listener:
	private IPartListener partListener = new IPartListener() {
		public void partOpened(IWorkbenchPart part) {
			setPart(part);
		}
		public void partClosed(IWorkbenchPart part) {
			if (part==editor) {
				setNetwork(null);
				editor = null;
			}
		}
		public void partActivated(IWorkbenchPart part) {
		}
		public void partBroughtToTop(IWorkbenchPart part) {
		}
		public void partDeactivated(IWorkbenchPart part) {
		}
	};
	
	// Page listener:
	private IPageListener pageListener = new IPageListener() {
		public void pageOpened(IWorkbenchPage page) {
			page.addPartListener(partListener);
		}
		public void pageClosed(IWorkbenchPage page) {
			page.removePartListener(partListener);
		}
		public void pageActivated(IWorkbenchPage page) {
		}
	};
	
	// Table listener for capturing double-clicks:
	private SelectionListener tableListener = new SelectionListener() {
		public void widgetDefaultSelected(SelectionEvent e) {
			handleDoubleClick((TableItem) e.item);
		}
		public void widgetSelected(SelectionEvent e) {
		}
	};
	
	// Network listener:
	private Adapter networkListener = new AdapterImpl() {
		public void notifyChanged(Notification msg) {
			Display.getDefault().asyncExec(new Runnable() {
				public void run() {
					updateTitle();
				}
			});
		}
	};
	
	// Key listener:
	private KeyListener keyListener = new KeyListener() {
		public void keyPressed(KeyEvent e) {
		}
		public void keyReleased(KeyEvent e) {
			if (SWT.DEL==e.character) {
				handleDelete();
			}
		}
	};

}